﻿#include "udpserver.h"
#include "globalstatic.h"
#include "globalsymbol.h"

#include "sdk/QsLog/include/QsLog.h"

#include <QDebug>
#include <QDateTime>
#include <QThread>
#include <QtMath>

UdpServer::UdpServer(QSettings* settings, QObject *parent) : QObject(parent),
    m_settings(settings),
    m_udpSocket(nullptr)
{
    // 初始化
    init();
}

UdpServer::~UdpServer()
{
    if (m_udpSocket != nullptr) {
        m_udpSocket->close();
        delete m_udpSocket;
        m_udpSocket = nullptr;
    }
}

void UdpServer::init()
{
    m_settings->beginGroup("Udp");
    QString localHost = m_settings->value("localHost").toString();
    quint32 localPort = m_settings->value("localPort").toUInt();
    QString remoteHost = m_settings->value("remoteHost").toString();
    quint32 remotePort = m_settings->value("remotePort").toUInt();
    m_settings->endGroup();

    m_localHost = QHostAddress(localHost);
    m_localPort = static_cast<quint16>(localPort);
    m_remoteHost = QHostAddress(remoteHost);
    m_remotePort = static_cast<quint16>(remotePort);
}

void UdpServer::fetch()
{
    if (m_recvBuffer.length() < RECV_FRAME_LENGTH)
        return;

    bool findHeader = false;
    int length = m_recvBuffer.length() - 1;
    for (int i = 0; i < length; i++) {
        if ( (static_cast<unsigned char>(m_recvBuffer.at(i)) == 0x90) && static_cast<unsigned char>(m_recvBuffer.at(i + 1)) == 0xE8) {
            // 删除帧头之前的无效数据
            m_recvBuffer.remove(0, i);
            findHeader = true;
            break;
        }
    }

    if (!findHeader) {
        if (m_recvBuffer.length() > RECV_FRAME_LENGTH * 4)
            m_recvBuffer.clear();
        if (m_recvBuffer.length() < RECV_FRAME_LENGTH)
            return;
    }

    length = ((m_recvBuffer.at(3) & 0xFF) << 8) | (m_recvBuffer.at(2) & 0xFF);
    if (m_recvBuffer.length() < length)
        return;


    // 流程想定下发: 计划码, 科目编号, 故障数量, 故障编号
    QByteArray planCode = m_recvBuffer.mid(30, 4);
    GlobalStatic::setPlanCode(planCode);

    QByteArray course = m_recvBuffer.mid(34, 6);
    QString courseNumber = QLatin1String(course);
    GlobalStatic::setCourseNumber(courseNumber);

    unsigned short faultCount = ((m_recvBuffer.at(41) & 0xFF) << 8) | (m_recvBuffer.at(40) & 0xFF);
    GlobalStatic::setFaultCount(faultCount);
    GlobalStatic::clearFaultCode();
    GlobalStatic::clearFaultScore();
    if (faultCount > 0) {
        for (int i = 0; i < faultCount; i++) {
            QString faultCode = m_recvBuffer.mid(42 + i * 12, 12);
            GlobalStatic::appendFaultCode(faultCode);
            GlobalStatic::setFaultScore(faultCode, 0);
        }
    }
    GlobalStatic::resetTrainStep();
    // 训练考试开始
    GlobalStatic::setTraining(true);
    GlobalStatic::setTrainStartTime(QDateTime::currentSecsSinceEpoch());

    //qDebug() << "*****" << m_recvBuffer.length() << planCode.toHex(' ') << courseNumber << faultCount;

    // 上报训练开始
    trainStart();

    // 清空重新接收
    m_recvBuffer.remove(0, length);

    // 科目考试开始
    emit sigCourseTrain(courseNumber);
}

void UdpServer::trainStart()
{
    unsigned short msgLength = 34;  // 消息长度
    unsigned short msgSrc = 107;  // 信源
    unsigned short msgDest = 100;  // 信宿
    QDate date = QDate::currentDate();  // 日期
    QTime time = QTime::currentTime();  // 时间
    int iTime = ( ( time.hour() * 60 + time.minute() ) * 60 + time.second() ) * 1000 + time.msec();
    unsigned short sendFrameCount = GlobalStatic::sendFrameCount();  // 帧计数

    QByteArray data(msgLength, 0x0);
    data[0] = static_cast<unsigned char>(0x90);
    data[1] = static_cast<unsigned char>(0xE8);
    data[2] = static_cast<unsigned char>(msgLength & 0xFF);  // 消息长度
    data[3] = static_cast<unsigned char>((msgLength >> 8) & 0xFF);
    data[4] = 0x07;  // 帧类型: 0x07
    data[5] = 0x00;  // 重传次数
    data[6] = 0x01;  // 帧确认标记
    data[7] = static_cast<unsigned char>(msgSrc & 0xFF);  // 信源
    data[8] = static_cast<unsigned char>((msgSrc >> 8) & 0xFF);
    data[9] = static_cast<unsigned char>(msgDest & 0xFF);  // 信宿
    data[10] = static_cast<unsigned char>((msgDest >> 8) & 0xFF);
    data[11] = static_cast<unsigned char>(date.year() & 0xFF);  // 发送日期: 年
    data[12] = static_cast<unsigned char>((date.year() >> 8) & 0xFF);
    data[13] = static_cast<unsigned char>(date.month());  // 发送日期: 月
    data[14] = static_cast<unsigned char>(date.day());  // 发送日期: 日
    data[15] = static_cast<unsigned char>(iTime & 0xFF);  // 发送日期: 时间
    data[16] = static_cast<unsigned char>((iTime >> 8) & 0xFF);
    data[17] = static_cast<unsigned char>((iTime >> 16) & 0xFF);
    data[18] = static_cast<unsigned char>((iTime >> 24) & 0xFF);
    data[19] = static_cast<unsigned char>(sendFrameCount & 0xFF);  // 帧计数
    data[20] = static_cast<unsigned char>((sendFrameCount >> 8) & 0xFF);
    data[21] = 0x04;  // 信息字个数
    data[22] = 0x00;
    data[23] = 0x01;  // 信息类型: 0x01
    data[24] = 0x00;  // 备用字段
    data[25] = 0x00;
    data[26] = 0x00;
    data[27] = 0x00;
    data[28] = 0x00;
    data[29] = GlobalStatic::getSumCheck(data, 0, 29);  // 校验和
    data[30] = GlobalStatic::planCode().at(0);
    data[31] = GlobalStatic::planCode().at(1);
    data[32] = GlobalStatic::planCode().at(2);
    data[33] = GlobalStatic::planCode().at(3);

    qint64 count = m_udpSocket->writeDatagram(data, m_remoteHost, m_remotePort);
    if (count <= 0) {
        QLOG_ERROR() << "****udp socket write(start) failed: " << m_udpSocket->errorString();
    }

    // 帧计数递增
    GlobalStatic::increaseSendFrameCount();
}

void UdpServer::onListen()
{
    // 创建socket对象
    m_udpSocket = new QUdpSocket;
    // 绑定地址和端口
    bool ret = m_udpSocket->bind(m_localPort);
    if (!ret) {
        QLOG_ERROR() << "****udp socket(server) bind failed: " << m_udpSocket->errorString();
        return;
    }

    // 建立UDP监听
    if (m_udpSocket->open(QUdpSocket::ReadWrite)){
        connect(m_udpSocket, &QUdpSocket::readyRead, this, &UdpServer::onRead);
        //QLOG_INFO() << "****udp socket listened on port: " << m_localHost.toString() << m_localPort;
    } else {
        QLOG_ERROR() << "****udp socket(server) open failed: " << m_udpSocket->errorString();
    }
}

void UdpServer::onRead()
{
    while (m_udpSocket->hasPendingDatagrams()) {
        QByteArray data;
        data.resize(static_cast<int>(m_udpSocket->pendingDatagramSize()));
        qint64 count = m_udpSocket->readDatagram(data.data(), data.length());
        if (count > 0){
            //qInfo() << "****udp socket read: " << count << ", " << data.toHex();
            m_recvBuffer.append(data);

            // 提取有效帧
            fetch();
        }
    }
}

void UdpServer::onWrite(const QByteArray data)
{
    qint64 count = m_udpSocket->writeDatagram(data, m_remoteHost, m_remotePort);
    if (count <= 0) {
        QLOG_ERROR() << "****udp socket write failed: " << m_udpSocket->errorString();
    }
}

void UdpServer::onHeartBeat()
{
    unsigned short msgLength = 30;  // 消息长度
    unsigned short msgSrc = 107;  // 信源
    unsigned short msgDest = 100;  // 信宿
    QDate date = QDate::currentDate();  // 日期
    QTime time = QTime::currentTime();  // 时间
    int iTime = ( ( time.hour() * 60 + time.minute() ) * 60 + time.second() ) * 1000 + time.msec();
    static unsigned short frameCount = 0;  // 帧计数

    QByteArray data(msgLength, 0x0);
    data[0] = static_cast<unsigned char>(0x90);
    data[1] = static_cast<unsigned char>(0xE8);
    data[2] = static_cast<unsigned char>(msgLength & 0xFF);  // 消息长度
    data[3] = static_cast<unsigned char>((msgLength >> 8) & 0xFF);
    data[4] = 0x07;  // 帧类型: 0x07
    data[5] = 0x00;  // 重传次数
    data[6] = 0x01;  // 帧确认标记
    data[7] = static_cast<unsigned char>(msgSrc & 0xFF);  // 信源
    data[8] = static_cast<unsigned char>((msgSrc >> 8) & 0xFF);
    data[9] = static_cast<unsigned char>(msgDest & 0xFF);  // 信宿
    data[10] = static_cast<unsigned char>((msgDest >> 8) & 0xFF);
    data[11] = static_cast<unsigned char>(date.year() & 0xFF);  // 发送日期: 年
    data[12] = static_cast<unsigned char>((date.year() >> 8) & 0xFF);
    data[13] = static_cast<unsigned char>(date.month());  // 发送日期: 月
    data[14] = static_cast<unsigned char>(date.day());  // 发送日期: 日
    data[15] = static_cast<unsigned char>(iTime & 0xFF);  // 发送日期: 时间
    data[16] = static_cast<unsigned char>((iTime >> 8) & 0xFF);
    data[17] = static_cast<unsigned char>((iTime >> 16) & 0xFF);
    data[18] = static_cast<unsigned char>((iTime >> 24) & 0xFF);
    data[19] = static_cast<unsigned char>(frameCount & 0xFF);  // 帧计数
    data[20] = static_cast<unsigned char>((frameCount >> 8) & 0xFF);
    data[21] = 0x00;  // 信息字个数
    data[22] = 0x00;
    data[23] = 0x03;  // 信息类型: 0x03
    data[24] = 0x00;  // 备用字段
    data[25] = 0x00;
    data[26] = 0x00;
    data[27] = 0x00;
    data[28] = 0x00;
    data[29] = GlobalStatic::getSumCheck(data, 0, 29);  // 校验和

    qint64 count = m_udpSocket->writeDatagram(data, m_remoteHost, m_remotePort);
    if (count <= 0) {
        QLOG_ERROR() << "****udp socket write(heartbeat) failed: " << m_udpSocket->errorString();
    }

    // 帧计数递增
    frameCount++;
}

void UdpServer::onTrainEnd()
{
    unsigned short msgLength = 34;  // 消息长度
    unsigned short msgSrc = 107;  // 信源
    unsigned short msgDest = 100;  // 信宿
    QDate date = QDate::currentDate();  // 日期
    QTime time = QTime::currentTime();  // 时间
    int iTime = ( ( time.hour() * 60 + time.minute() ) * 60 + time.second() ) * 1000 + time.msec();
    unsigned short sendFrameCount = GlobalStatic::sendFrameCount();  // 帧计数

    QByteArray data(msgLength, 0x0);
    data[0] = static_cast<unsigned char>(0x90);
    data[1] = static_cast<unsigned char>(0xE8);
    data[2] = static_cast<unsigned char>(msgLength & 0xFF);  // 消息长度
    data[3] = static_cast<unsigned char>((msgLength >> 8) & 0xFF);
    data[4] = 0x07;  // 帧类型: 0x07
    data[5] = 0x00;  // 重传次数
    data[6] = 0x01;  // 帧确认标记
    data[7] = static_cast<unsigned char>(msgSrc & 0xFF);  // 信源
    data[8] = static_cast<unsigned char>((msgSrc >> 8) & 0xFF);
    data[9] = static_cast<unsigned char>(msgDest & 0xFF);  // 信宿
    data[10] = static_cast<unsigned char>((msgDest >> 8) & 0xFF);
    data[11] = static_cast<unsigned char>(date.year() & 0xFF);  // 发送日期: 年
    data[12] = static_cast<unsigned char>((date.year() >> 8) & 0xFF);
    data[13] = static_cast<unsigned char>(date.month());  // 发送日期: 月
    data[14] = static_cast<unsigned char>(date.day());  // 发送日期: 日
    data[15] = static_cast<unsigned char>(iTime & 0xFF);  // 发送日期: 时间
    data[16] = static_cast<unsigned char>((iTime >> 8) & 0xFF);
    data[17] = static_cast<unsigned char>((iTime >> 16) & 0xFF);
    data[18] = static_cast<unsigned char>((iTime >> 24) & 0xFF);
    data[19] = static_cast<unsigned char>(sendFrameCount & 0xFF);  // 帧计数
    data[20] = static_cast<unsigned char>((sendFrameCount >> 8) & 0xFF);
    data[21] = 0x04;  // 信息字个数
    data[22] = 0x00;
    data[23] = 0x02;  // 信息类型: 0x02
    data[24] = 0x00;  // 备用字段
    data[25] = 0x00;
    data[26] = 0x00;
    data[27] = 0x00;
    data[28] = 0x00;
    data[29] = GlobalStatic::getSumCheck(data, 0, 29);  // 校验和
    data[30] = GlobalStatic::planCode().at(0);
    data[31] = GlobalStatic::planCode().at(1);
    data[32] = GlobalStatic::planCode().at(2);
    data[33] = GlobalStatic::planCode().at(3);

    qint64 count = m_udpSocket->writeDatagram(data, m_remoteHost, m_remotePort);
    if (count <= 0) {
        QLOG_ERROR() << "****udp socket write(end) failed: " << m_udpSocket->errorString();
    }

    // 帧计数递增
    GlobalStatic::increaseSendFrameCount();
}

void UdpServer::onStepNotify(const QString stepCode)
{
    unsigned short msgLength = 49;  // 消息长度
    unsigned short msgSrc = 107;  // 信源
    unsigned short msgDest = 100;  // 信宿
    QDate date = QDate::currentDate();  // 日期
    QTime time = QTime::currentTime();  // 时间
    int iTime = ( ( time.hour() * 60 + time.minute() ) * 60 + time.second() ) * 1000 + time.msec();
    unsigned short sendFrameCount = GlobalStatic::sendFrameCount();  // 帧计数

    // 科目编号
    QByteArray courseNumberArray = GlobalStatic::courseNumber().toLatin1();
    // 操作编号
    QByteArray stepCodeArray = stepCode.toLatin1();

    QByteArray data(msgLength, 0x0);
    data[0] = static_cast<unsigned char>(0x90);
    data[1] = static_cast<unsigned char>(0xE8);
    data[2] = static_cast<unsigned char>(msgLength & 0xFF);  // 消息长度
    data[3] = static_cast<unsigned char>((msgLength >> 8) & 0xFF);
    data[4] = 0x06;  // 帧类型: 0x06
    data[5] = 0x00;  // 重传次数
    data[6] = 0x01;  // 帧确认标记
    data[7] = static_cast<unsigned char>(msgSrc & 0xFF);  // 信源
    data[8] = static_cast<unsigned char>((msgSrc >> 8) & 0xFF);
    data[9] = static_cast<unsigned char>(msgDest & 0xFF);  // 信宿
    data[10] = static_cast<unsigned char>((msgDest >> 8) & 0xFF);
    data[11] = static_cast<unsigned char>(date.year() & 0xFF);  // 发送日期: 年
    data[12] = static_cast<unsigned char>((date.year() >> 8) & 0xFF);
    data[13] = static_cast<unsigned char>(date.month());  // 发送日期: 月
    data[14] = static_cast<unsigned char>(date.day());  // 发送日期: 日
    data[15] = static_cast<unsigned char>(iTime & 0xFF);  // 发送日期: 时间
    data[16] = static_cast<unsigned char>((iTime >> 8) & 0xFF);
    data[17] = static_cast<unsigned char>((iTime >> 16) & 0xFF);
    data[18] = static_cast<unsigned char>((iTime >> 24) & 0xFF);
    data[19] = static_cast<unsigned char>(sendFrameCount & 0xFF);  // 帧计数
    data[20] = static_cast<unsigned char>((sendFrameCount >> 8) & 0xFF);
    data[21] = 0x13;  // 信息字个数
    data[22] = 0x00;
    data[23] = 0x03;  // 信息类型: 0x03
    data[24] = 0x00;  // 备用字段
    data[25] = 0x00;
    data[26] = 0x00;
    data[27] = 0x00;
    data[28] = 0x00;
    data[29] = GlobalStatic::getSumCheck(data, 0, 29);  // 校验和
    // 科目编号
    for (int i = 0; i < 6; i++) {
        if (i < courseNumberArray.size()) {
            data[30 + i] = courseNumberArray.at(i);
        }
    }
    // 操作编号
    for (int i = 0; i < 11; i++) {
        if (i < stepCodeArray.size()) {
            data[36 + i] = stepCodeArray.at(i);
        }
    }
    // 操作正确性
    data[47] = 0x01;
    data[48] = 0x00;

    qint64 count = m_udpSocket->writeDatagram(data, m_remoteHost, m_remotePort);
    if (count <= 0) {
        QLOG_ERROR() << "****udp socket write(step notify) failed: " << m_udpSocket->errorString();
    }

    // 帧计数递增
    GlobalStatic::increaseSendFrameCount();
}

void UdpServer::onFaultNotify(const QString faultCode, const int result)
{
    unsigned short msgLength = 50;  // 消息长度
    unsigned short msgSrc = 107;  // 信源
    unsigned short msgDest = 100;  // 信宿
    QDate date = QDate::currentDate();  // 日期
    QTime time = QTime::currentTime();  // 时间
    int iTime = ( ( time.hour() * 60 + time.minute() ) * 60 + time.second() ) * 1000 + time.msec();
    unsigned short sendFrameCount = GlobalStatic::sendFrameCount();  // 帧计数

    // 科目编号
    QByteArray courseNumberArray = GlobalStatic::courseNumber().toLatin1();
    // 故障编号
    QByteArray faultCodeArray = faultCode.toLatin1();
    // 计算故障得分: 0错误, 1正确
    double averageFaultScore = 10. / GlobalStatic::faultCount();
    switch (result) {
    case 0:
        GlobalStatic::setFaultScore(faultCode, 0);
        break;
    case 1: {
        int index = GlobalStatic::faultCodes().indexOf(faultCode);
        if (index == GlobalStatic::faultCount() - 1) {
            GlobalStatic::setFaultScore( faultCode, 10 - static_cast<int>(averageFaultScore) * (GlobalStatic::faultCount() - 1) );

        } else {
            GlobalStatic::setFaultScore(faultCode, averageFaultScore);
        }
    }
        break;
    default:
        GlobalStatic::setFaultScore(faultCode, 0);
        break;
    }
    short faultScore = static_cast<short>(GlobalStatic::faultScore(faultCode));

    QByteArray data(msgLength, 0x0);
    data[0] = static_cast<unsigned char>(0x90);
    data[1] = static_cast<unsigned char>(0xE8);
    data[2] = static_cast<unsigned char>(msgLength & 0xFF);  // 消息长度
    data[3] = static_cast<unsigned char>((msgLength >> 8) & 0xFF);
    data[4] = 0x06;  // 帧类型: 0x06
    data[5] = 0x00;  // 重传次数
    data[6] = 0x01;  // 帧确认标记
    data[7] = static_cast<unsigned char>(msgSrc & 0xFF);  // 信源
    data[8] = static_cast<unsigned char>((msgSrc >> 8) & 0xFF);
    data[9] = static_cast<unsigned char>(msgDest & 0xFF);  // 信宿
    data[10] = static_cast<unsigned char>((msgDest >> 8) & 0xFF);
    data[11] = static_cast<unsigned char>(date.year() & 0xFF);  // 发送日期: 年
    data[12] = static_cast<unsigned char>((date.year() >> 8) & 0xFF);
    data[13] = static_cast<unsigned char>(date.month());  // 发送日期: 月
    data[14] = static_cast<unsigned char>(date.day());  // 发送日期: 日
    data[15] = static_cast<unsigned char>(iTime & 0xFF);  // 发送日期: 时间
    data[16] = static_cast<unsigned char>((iTime >> 8) & 0xFF);
    data[17] = static_cast<unsigned char>((iTime >> 16) & 0xFF);
    data[18] = static_cast<unsigned char>((iTime >> 24) & 0xFF);
    data[19] = static_cast<unsigned char>(sendFrameCount & 0xFF);  // 帧计数
    data[20] = static_cast<unsigned char>((sendFrameCount >> 8) & 0xFF);
    data[21] = 0x13;  // 信息字个数
    data[22] = 0x00;
    data[23] = 0x04;  // 信息类型: 0x04
    data[24] = 0x00;  // 备用字段
    data[25] = 0x00;
    data[26] = 0x00;
    data[27] = 0x00;
    data[28] = 0x00;
    data[29] = GlobalStatic::getSumCheck(data, 0, 29);  // 校验和
    // 科目编号
    for (int i = 0; i < 6; i++) {
        if (i < courseNumberArray.size()) {
            data[30 + i] = courseNumberArray.at(i);
        }
    }
    // 操作编号
    for (int i = 0; i < 12; i++) {
        if (i < faultCodeArray.size()) {
            data[36 + i] = faultCodeArray.at(i);
        }
    }
    // 得分
    data[48] = static_cast<unsigned char>(faultScore & 0xFF);  // 得分
    data[49] = static_cast<unsigned char>((faultScore >> 8) & 0xFF);

    qint64 count = m_udpSocket->writeDatagram(data, m_remoteHost, m_remotePort);
    if (count <= 0) {
        QLOG_ERROR() << "****udp socket write(fault notify) failed: " << m_udpSocket->errorString();
    }

    // 帧计数递增
    GlobalStatic::increaseSendFrameCount();
}

void UdpServer::onCourseScore()
{
    unsigned short msgLength = 38;  // 消息长度
    unsigned short msgSrc = 107;  // 信源
    unsigned short msgDest = 100;  // 信宿
    QDate date = QDate::currentDate();  // 日期
    QTime time = QTime::currentTime();  // 时间
    int iTime = ( ( time.hour() * 60 + time.minute() ) * 60 + time.second() ) * 1000 + time.msec();
    unsigned short sendFrameCount = GlobalStatic::sendFrameCount();  // 帧计数

    // 科目编号
    QByteArray courseNumberArray = GlobalStatic::courseNumber().toLatin1();
    // 得分:
    int totalStepCount = GlobalStatic::getTotalStepCount();
    int trainedStepCount = GlobalStatic::getTrainedStepCount();
    short courseScore = static_cast<short>( qCeil(100. / totalStepCount * trainedStepCount) );
    //qDebug() << "********" << totalStepCount << trainedStepCount;


    QByteArray data(msgLength, 0x0);
    data[0] = static_cast<unsigned char>(0x90);
    data[1] = static_cast<unsigned char>(0xE8);
    data[2] = static_cast<unsigned char>(msgLength & 0xFF);  // 消息长度
    data[3] = static_cast<unsigned char>((msgLength >> 8) & 0xFF);
    data[4] = 0x06;  // 帧类型: 0x06
    data[5] = 0x00;  // 重传次数
    data[6] = 0x01;  // 帧确认标记
    data[7] = static_cast<unsigned char>(msgSrc & 0xFF);  // 信源
    data[8] = static_cast<unsigned char>((msgSrc >> 8) & 0xFF);
    data[9] = static_cast<unsigned char>(msgDest & 0xFF);  // 信宿
    data[10] = static_cast<unsigned char>((msgDest >> 8) & 0xFF);
    data[11] = static_cast<unsigned char>(date.year() & 0xFF);  // 发送日期: 年
    data[12] = static_cast<unsigned char>((date.year() >> 8) & 0xFF);
    data[13] = static_cast<unsigned char>(date.month());  // 发送日期: 月
    data[14] = static_cast<unsigned char>(date.day());  // 发送日期: 日
    data[15] = static_cast<unsigned char>(iTime & 0xFF);  // 发送日期: 时间
    data[16] = static_cast<unsigned char>((iTime >> 8) & 0xFF);
    data[17] = static_cast<unsigned char>((iTime >> 16) & 0xFF);
    data[18] = static_cast<unsigned char>((iTime >> 24) & 0xFF);
    data[19] = static_cast<unsigned char>(sendFrameCount & 0xFF);  // 帧计数
    data[20] = static_cast<unsigned char>((sendFrameCount >> 8) & 0xFF);
    data[21] = 0x13;  // 信息字个数
    data[22] = 0x00;
    data[23] = 0x05;  // 信息类型: 0x05
    data[24] = 0x00;  // 备用字段
    data[25] = 0x00;
    data[26] = 0x00;
    data[27] = 0x00;
    data[28] = 0x00;
    data[29] = GlobalStatic::getSumCheck(data, 0, 29);  // 校验和
    // 科目编号
    for (int i = 0; i < 6; i++) {
        if (i < courseNumberArray.size()) {
            data[30 + i] = courseNumberArray.at(i);
        }
    }
    // 得分
    data[36] = static_cast<unsigned char>(courseScore & 0xFF);  // 得分
    data[37] = static_cast<unsigned char>((courseScore >> 8) & 0xFF);

    qint64 count = m_udpSocket->writeDatagram(data, m_remoteHost, m_remotePort);
    if (count <= 0) {
        QLOG_ERROR() << "****udp socket write(course score) failed: " << m_udpSocket->errorString();
    }

    // 帧计数递增
    GlobalStatic::increaseSendFrameCount();
}
